<?php
class File
{
    private string $filePath;

    public function __construct(string $path)
    {
        $this->filePath = $path;
    }

    /**
     * 获取文件或目录名
     *
     * @return string 文件或目录名
     */
    public function getName(): string
    {
        return pathinfo($this->filePath, PATHINFO_FILENAME);
    }

    /**
     * 获取父目录路径
     *
     * @return string|null 父目录路径，如果是根目录则返回 null
     */
    public function getParent(): ?string
    {
        return dirname($this->filePath);
    }

    /**
     * 获取文件后缀
     *
     * @return string|null 文件后缀，如果是目录则返回 null
     */
    public function getFileType(): ?string
    {
        return $this->isFile() ? pathinfo($this->filePath, PATHINFO_EXTENSION) : null;
    }

    /**
     * 创建目录
     *
     * @return bool 创建成功返回 true，否则返回 false
     */
    public function mkdir(): bool
    {
        if (!$this->exists()) {
            return mkdir($this->filePath, 0777, true);
        }
        return false;
    }

    /**
     * 重命名文件或目录
     *
     * @param string $newName 新的文件或目录名
     * @return bool 重命名成功返回 true，否则返回 false
     */
    public function renameTo(string $newName): bool
    {
        $newPath = $this->getParent() . DIRECTORY_SEPARATOR . $newName;
        return rename($this->filePath, $newPath);
    }

    /**
     * 删除文件或目录
     *
     * @return bool 删除成功返回 true，否则返回 false
     */
    public function delete(): bool
    {
        if ($this->isDirectory()) {
            $files = array_diff(scandir($this->filePath), ['.', '..']);
            foreach ($files as $file) {
                (new File($this->filePath . DIRECTORY_SEPARATOR . $file))->delete();
            }
            return rmdir($this->filePath);
        } elseif ($this->isFile()) {
            return unlink($this->filePath);
        }
        return false;
    }


    /**
     * 判断是否为文件
     *
     * @return bool 如果是文件则返回 true，否则返回 false
     */
    public function isFile(): bool
    {
        return is_file($this->filePath);
    }

    /**
     * 判断是否为目录
     *
     * @return bool 如果是目录则返回 true，否则返回 false
     */
    public function isDirectory(): bool
    {
        return is_dir($this->filePath);
    }

    /**
     * 创建新文件
     *
     * @return bool 创建成功返回 true，否则返回 false
     */
    public function createNewFile(): bool
    {
        if (!$this->isFile()) {
            $handle = fopen($this->filePath, 'w');
            if ($handle !== false) {
                fclose($handle);
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件大小
     *
     * @return int|null 文件大小，如果文件不存在则返回 null
     */
    public function size(): ?int
    {
        return $this->isFile() ? filesize($this->filePath) : null;
    }

    /**
     * 比较文件路径
     *
     * @param File $file 另一个文件对象
     * @return int 负值、零、正值分别表示小于、等于、大于
     */
    public function compareTo(File $file): int
    {
        return strcmp($this->filePath, $file->getPath());
    }

    /**
     * 比较是否相等
     *
     * @param File $file 另一个文件对象
     * @return bool 如果路径相等则返回 true，否则返回 false
     */
    public function equals(File $file): bool
    {
        return $this->filePath === $file->getPath();
    }

    /**
     * 判断文件是否存在
     *
     * @return bool 如果文件存在则返回 true，否则返回 false
     */
    public function exists(): bool
    {
        return file_exists($this->filePath);
    }

    /**
     * 获取子文件对象数组
     *
     * @return ArrayList<File> 子文件对象数组
     */
    public function getFileChildren(): ArrayList
    {
        $children = [];

        if ($this->isDirectory()) {
            $files = scandir($this->filePath);
            foreach ($files as $filename) {
                $childPath = $this->filePath . DIRECTORY_SEPARATOR . $filename;
                if (is_file($childPath) && $filename !== '.' && $filename !== '..') {
                    $children[] = new File($childPath);
                }
            }
        }

        return ArrayList::toArrayList($children);
    }

    /**
     * 获取子目录对象数组
     *
     * @return ArrayList<File> 子目录对象数组
     */
    public function getDirChildren(): ArrayList
    {
        $children = [];

        if ($this->isDirectory()) {
            $files = scandir($this->filePath);
            foreach ($files as $file) {
                $childPath = $this->filePath . DIRECTORY_SEPARATOR . $file;
                if (is_dir($childPath) && $file !== '.' && $file !== '..') {
                    $children[] = new File($childPath);
                }
            }
        }

        return ArrayList::toArrayList($children);
    }


    /**
     * 复制文件或目录
     *
     * @param string $destinationPath 目标路径
     * @param string|null $newFilename 新的文件名，可选
     * @param bool $isOverwrite 是否覆盖已存在的文件，可选，默认为 true
     * @return bool 复制成功返回 true，否则返回 false
     */
    public function copy(string $destinationPath, ?string $newFilename = null, bool $isOverwrite = false): bool
    {
        if (!$this->exists()) {
            return false; // 源文件或目录不存在
        }

        if (!file_exists($destinationPath)) {
            mkdir($destinationPath, 0777, true); // 创建目标路径
        }

        if ($this->isDirectory()) {
            return $this->copyDirectory($destinationPath, $newFilename, $isOverwrite);
        } elseif ($this->isFile()) {
            return $this->copyFile($destinationPath, $newFilename, $isOverwrite);
        }

        return false;
    }

    /**
     * 复制目录
     *
     * @param string $destinationPath 目标路径
     * @param string|null $newFilename 新的文件名，可选
     * @param bool $isOverwrite 是否覆盖已存在的文件，可选，默认为 true
     * @return bool 复制成功返回 true，否则返回 false
     */
    private function copyDirectory(string $destinationPath, ?string $newFilename, bool $isOverwrite): bool
    {
        if (!file_exists($destinationPath)) {
            mkdir($destinationPath, 0777, true);
        }

        $files = scandir($this->filePath);
        foreach ($files as $file) {
            if ($file !== '.' && $file !== '..') {
                $source = $this->filePath . DIRECTORY_SEPARATOR . $file;
                $destination = $destinationPath . DIRECTORY_SEPARATOR . ($newFilename ?: $file);

                $this->copyRecursive($source, $destination, $isOverwrite);
            }
        }

        return true;
    }

    /**
     * 复制文件
     *
     * @param string $destinationPath 目标路径
     * @param string|null $newFilename 新的文件名，可选
     * @param bool $isOverwrite 是否覆盖已存在的文件，可选，默认为 true
     * @return bool 复制成功返回 true，否则返回 false
     */
    private function copyFile(string $destinationPath, ?string $newFilename, bool $isOverwrite): bool
    {
        $sourceExtension = $this->getFileType();

        // 如果 newFilename 包含文件后缀，则使用它；否则保留源文件的文件后缀
        $destinationFilename = $newFilename ?: $this->getName();
        if (pathinfo($destinationFilename, PATHINFO_EXTENSION) === '') {
            $destinationFilename .= '.' . $sourceExtension;
        }

        $destination = $destinationPath . DIRECTORY_SEPARATOR . $destinationFilename;

        if (!$isOverwrite && file_exists($destination)) {
            return false; // 目标文件已存在且不允许覆盖
        }

        return copy($this->filePath, $destination);
    }


    /**
     * 递归复制文件或目录
     *
     * @param string $source 源路径
     * @param string $destination 目标路径
     * @return bool 复制成功返回 true，否则返回 false
     */
    private function copyRecursive(string $source, string $destination): bool
    {
        if (is_dir($source)) {
            if (!file_exists($destination)) {
                mkdir($destination, 0777, true);
            }

            $files = scandir($source);
            foreach ($files as $file) {
                if ($file !== '.' && $file !== '..') {
                    $this->copyRecursive($source . DIRECTORY_SEPARATOR . $file, $destination . DIRECTORY_SEPARATOR . $file);
                }
            }

            return true;
        } elseif (is_file($source)) {
            return copy($source, $destination);
        }

        return false;
    }

    /**
     * 判断是否为上传的文件
     *
     * @param mixed $file
     * @return bool
     */
    public function isUploadedFile($file): bool
    {
        return is_array($file) && isset($file['tmp_name']) && is_uploaded_file($file['tmp_name']);
    }

    /**
     * 保存上传的文件
     *
     * @param mixed $file 上传的文件数据
     * @return bool 保存成功返回 true，否则返回 false
     */
    public function saveUploadedFile($file): bool
    {
        if ($this->isUploadedFile($file)) {
            return move_uploaded_file($file['tmp_name'], $this->filePath);
        }
        return false;
    }

    /**
     * 向文件写入内容
     *
     * @param string $content 要写入的内容
     * @return bool 写入成功返回 true，否则返回 false
     */
    public function write(string $content): bool
    {
        if ($this->isFile()){
            $handle = fopen($this->filePath, 'w');
            if ($handle === false) {
                return false; // 文件打开失败
            }
            $result = fwrite($handle, $content);
            fclose($handle);
            return $result !== false;
        }else{
            return false;
        }
    }

    public function getPath(): string
    {
        return $this->filePath;
    }
}


